using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Configuration;
using Subkismet.Properties;

namespace Subkismet.Services.GoogleSafeBrowsing
{
    /// <summary>
    /// Main implementation for Google Safe Browsing.
    /// </summary>
    public class GoogleSafeBrowsing
    {
        /// <summary>
        /// Returns the version of the implementation.
        /// </summary>
        static readonly string version = typeof(HttpClient).Assembly.GetName().Version.ToString();

        #region Public methods

        /// <summary>
        /// Updates a blacklist and synchronizes the local
        /// data with the Google server.
        /// </summary>
        /// <param name="listType">The blacklist type.</param>
        /// <returns>A Boolean value that specifies if the update process was successfull.</returns>
        public bool UpdateList(BlackListType listType)
        {
            Synchronizer sync = new Synchronizer(ConfigurationManager.AppSettings["apiKey"]);
            return sync.Update(listType);
        }

        /// <summary>
        /// Verifies all links in a comment and returns
        /// the number of phishing or malware links.
        /// </summary>
        /// <param name="comment">An IComment instance.</param>
        /// <param name="numberOfPhishingOccurs">The number of the phishing links (out).</param>
        /// <param name="numberOfMalwareOccurs">The number of the malware links (out).</param>
        /// <returns>A Boolean value that specifies if there was any bad link in the comment</returns>
        public bool CheckPost(IComment comment, out int numberOfPhishingOccurs,
            out int numberOfMalwareOccurs)
        {
            if (comment == null)
                throw new ArgumentNullException("comment", Resources.ArgumentNull_Generic);

            if (comment.Content == null)
                throw new ArgumentNullException("comment.Content", Resources.ArgumentNull_String);

            List<string> links = GetLinksFromText(comment.Content);

            if (comment.AuthorUrl != null)
                links.Add(comment.AuthorUrl.ToString());

            Synchronizer sync = new Synchronizer(ConfigurationManager.AppSettings["apiKey"]);

            sync.CheckLinks(links, out numberOfPhishingOccurs, out numberOfMalwareOccurs);

            if ((numberOfPhishingOccurs + numberOfMalwareOccurs) > 0)
                return true;

            return false;
        }

        /// <summary>
        /// Checks a single link.
        /// </summary>
        /// <param name="link">The string value of the link.</param>
        /// <returns>A CheckResultType enumerator specifying the type of the link.</returns>
        public CheckResultType CheckLink(string link)
        {
            if (link == null)
                throw new ArgumentNullException("link", Resources.ArgumentNull_String);

            List<string> links = new List<string>();
            links.Add(link);

            Synchronizer sync = new Synchronizer(ConfigurationManager.AppSettings["apiKey"]);

            int numberOfPhishingOccurs = 0;
            int numberOfMalwareOccurs = 0;

            sync.CheckLinks(links, out numberOfPhishingOccurs, out numberOfMalwareOccurs);

            if (numberOfPhishingOccurs > 0)
                return CheckResultType.Phishing;
            if (numberOfMalwareOccurs > 0)
                return CheckResultType.Malware;

            return CheckResultType.Safe;
        }

        /// <summary>
        /// Checks a list of links.
        /// </summary>
        /// <param name="links">A list of string value for the links.</param>
        /// <param name="numberOfPhishingOccurs">The number of the phishing links (out).</param>
        /// <param name="numberOfMalwareOccurs">The number of the malware links (out).</param>
        /// <returns>A Boolean value specifying if there was any bad link in the list.</returns>
        public bool CheckLinks(List<string> links, out int numberOfPhishingOccurs,
            out int numberOfMalwareOccurs)
        {
            if (links == null)
                throw new ArgumentNullException("links", Resources.ArgumentNull_Generic);

            Synchronizer sync = new Synchronizer(ConfigurationManager.AppSettings["apiKey"]);

            sync.CheckLinks(links, out numberOfPhishingOccurs, out numberOfMalwareOccurs);

            if ((numberOfPhishingOccurs + numberOfMalwareOccurs) > 0)
                return true;

            return false;
        }

        /// <summary>
        /// Returns the user agent text for the client.
        /// </summary>
        /// <param name="applicationName">The name of the application.</param>
        /// <returns>The string value of the user agent.</returns>
        public static string GetUserAgent(string applicationName)
        {
            if (applicationName == null)
                throw new ArgumentNullException("applicationName", Resources.ArgumentNull_String);

            return string.Format(CultureInfo.InvariantCulture,
                "{0}, Google Safe Browsing API, Implementation for .NET in Subkismet, Version: {1}",
                applicationName, version);
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Gets a list of links from a text that contains HTML codes.
        /// </summary>
        /// <param name="text">The text that contains HTML.</param>
        /// <returns>A list of strings that contains all links in the text.</returns>
        private List<string> GetLinksFromText(string text)
        {
            List<string> result = new List<string>();

            string pattern = @"(?<http>(http:[/][/]|www.)([a-z]|[A-Z]|[0-9]|[/.]|[~])*)";

            Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);

            MatchCollection matches = regex.Matches(text);

            foreach (Match match in matches)
            {
                result.Add(match.Value);
            }

            return result;
        }

        #endregion
    }
}
